{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# Handoffs\n",
        "\n",
        "Handoffs allow an agent to delegate tasks to another agent. This is particularly useful in scenarios where different agents specialize in distinct areas. For example, a customer support app might have agents that each specifically handle tasks like order status, refunds, FAQs, etc.\n",
        "\n",
        "Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named Refund Agent, the tool would be called transfer_to_refund_agent.\n",
        "\n",
        "[Learning Reference](https://openai.github.io/openai-agents-python/handoffs/)"
      ],
      "metadata": {
        "id": "Gzlfa596ZzW6"
      }
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PdKwzEluDBN7"
      },
      "source": [
        "## Install openai-agents SDK"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3QdkOviEB2ay"
      },
      "outputs": [],
      "source": [
        "!pip install -Uq openai-agents"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7yD91lz4DIAx"
      },
      "source": [
        "## Make your Notebook capable of running asynchronous functions.\n",
        "Both Jupyter notebooks and Python’s asyncio library utilize event loops, but they serve different purposes and can sometimes interfere with each other.\n",
        "\n",
        "The nest_asyncio library allows the existing event loop to accept nested event loops, enabling asyncio code to run within environments that already have an event loop, such as Jupyter notebooks.\n",
        "\n",
        "In summary, both Jupyter notebooks and Python’s asyncio library utilize event loops to manage asynchronous operations. When working within Jupyter notebooks, it’s essential to be aware of the existing event loop to effectively run asyncio code without conflicts."
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import nest_asyncio\n",
        "nest_asyncio.apply()"
      ],
      "metadata": {
        "id": "C8YXyIpiZ9v4"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Config"
      ],
      "metadata": {
        "id": "wQsVowow7ihQ"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "XnusaX_RWF22"
      },
      "outputs": [],
      "source": [
        "from pydantic import BaseModel\n",
        "from agents import (\n",
        "    AsyncOpenAI,\n",
        "    OpenAIChatCompletionsModel,\n",
        "    RunConfig\n",
        ")\n",
        "from google.colab import userdata\n"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "gemini_api_key = userdata.get(\"GEMINI_API_KEY\")\n",
        "\n",
        "\n",
        "# Check if the API key is present; if not, raise an error\n",
        "if not gemini_api_key:\n",
        "    raise ValueError(\"GEMINI_API_KEY is not set. Please ensure it is defined in your .env file.\")\n",
        "\n",
        "#Reference: https://ai.google.dev/gemini-api/docs/openai\n",
        "external_client = AsyncOpenAI(\n",
        "    api_key=gemini_api_key,\n",
        "    base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\",\n",
        ")\n",
        "\n",
        "model = OpenAIChatCompletionsModel(\n",
        "    model=\"gemini-2.0-flash\",\n",
        "    openai_client=external_client\n",
        ")\n",
        "\n",
        "config = RunConfig(\n",
        "    model=model,\n",
        "    model_provider=external_client,\n",
        "    tracing_disabled=True\n",
        ")"
      ],
      "metadata": {
        "id": "oPvcFwItoKqw"
      },
      "execution_count": 96,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Creating a handoff\n",
        "\n",
        "All agents have a handoffs param, which can either take an Agent directly, or a Handoff object that customizes the Handoff."
      ],
      "metadata": {
        "id": "L4gdpCV3G6mL"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import asyncio\n",
        "from agents import Agent, handoff, Runner"
      ],
      "metadata": {
        "id": "xK-Q9py2aSHq"
      },
      "execution_count": 94,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 1. Basic Usage"
      ],
      "metadata": {
        "id": "H69YnuGiaPcS"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "urdu_agent = Agent(\n",
        "    name=\"Urdu agent\",\n",
        "    instructions=\"You only speak Urdu.\"\n",
        ")\n",
        "\n",
        "english_agent = Agent(\n",
        "    name=\"English agent\",\n",
        "    instructions=\"You only speak English\"\n",
        ")\n",
        "\n",
        "triage_agent = Agent(\n",
        "    name=\"Triage agent\",\n",
        "    instructions=\"Handoff to the appropriate agent based on the language of the request.\",\n",
        "    handoffs=[urdu_agent, english_agent],\n",
        ")\n",
        "\n",
        "\n",
        "async def main(input: str):\n",
        "    result = await Runner.run(triage_agent, input=input, run_config=config)\n",
        "    print(result.final_output)\n"
      ],
      "metadata": {
        "id": "CGt8gkA6cX6_"
      },
      "execution_count": 112,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "asyncio.run(main(\"السلام عليكم\"))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "QitpUQUrdtFV",
        "outputId": "3772ccd0-a69b-4071-a905-1d2803502abe"
      },
      "execution_count": 113,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "وعلیکم السلام! آپ کیسے ہیں؟ کیا میں آپ کی کوئی مدد کرسکتا ہوں؟\n",
            "\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "asyncio.run(main(\"Hi\"))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ZPe1lF_SduCL",
        "outputId": "73362524-33e8-4411-86c0-34ec574fa32c"
      },
      "execution_count": 114,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Hi there! How can I help you today?\n",
            "\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 2. Customizing handoffs via the handoff() function"
      ],
      "metadata": {
        "id": "ueYtWnG2d19I"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from agents import Agent, handoff, RunContextWrapper\n",
        "\n",
        "urdu_agent = Agent(\n",
        "    name=\"Urdu agent\",\n",
        "    instructions=\"You only speak Urdu.\"\n",
        ")\n",
        "\n",
        "english_agent = Agent(\n",
        "    name=\"English agent\",\n",
        "    instructions=\"You only speak English\"\n",
        ")\n",
        "\n",
        "def on_handoff(agent: Agent, ctx: RunContextWrapper[None]):\n",
        "    agent_name = agent.name\n",
        "    print(\"--------------------------------\")\n",
        "    print(f\"Handing off to {agent_name}...\")\n",
        "    print(\"--------------------------------\")\n",
        "\n",
        "triage_agent = Agent(\n",
        "    name=\"Triage agent\",\n",
        "    instructions=\"Handoff to the appropriate agent based on the language of the request.\",\n",
        "    handoffs=[\n",
        "            handoff(urdu_agent, on_handoff=lambda ctx: on_handoff(urdu_agent, ctx)),\n",
        "            handoff(english_agent, on_handoff=lambda ctx: on_handoff(english_agent, ctx))\n",
        "    ],\n",
        ")\n",
        "\n",
        "\n",
        "async def main(input: str):\n",
        "    result = await Runner.run(triage_agent, input=input, run_config=config)\n",
        "    print(result.final_output)\n"
      ],
      "metadata": {
        "id": "M50noPNyd9e6"
      },
      "execution_count": 115,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "asyncio.run(main(\"السلام عليكم\"))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "lbTE7uxNeT78",
        "outputId": "6cb0dc69-eb9c-4d32-a9c6-3b3066c66459"
      },
      "execution_count": 116,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "--------------------------------\n",
            "Handing off to Urdu agent...\n",
            "--------------------------------\n",
            "وعلیکم السلام! آپ کیسے ہیں؟ میں آپ کی کیا مدد کر سکتا ہوں؟\n",
            "\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "asyncio.run(main(\"Hello, nice to meet you\"))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "b6X9XqQPeV_A",
        "outputId": "bd30d662-fd81-4179-a74d-c7649cd06ec9"
      },
      "execution_count": 118,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "--------------------------------\n",
            "Handing off to English agent...\n",
            "--------------------------------\n",
            "Nice to meet you too! How can I help you today?\n",
            "\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Self Assignment\n",
        "\n",
        "Implement the following with HandOffs Pattern:\n",
        "- Handoff Custom Inputs\n",
        "- Set an input_filter\n",
        "- Share info about handoffs in your agents\n",
        "- Implement Streaming With HandOff\n",
        "\n",
        "Here are some research references to get started:\n",
        "- https://openai.github.io/openai-agents-python/handoffs/\n",
        "- https://github.com/openai/openai-agents-python/blob/main/examples/handoffs/message_filter_streaming.py\n",
        "- https://github.com/openai/openai-agents-python/blob/main/examples/handoffs/message_filter.py"
      ],
      "metadata": {
        "id": "KxMKeAlTe393"
      }
    }
  ]
}